# -*- coding: utf-8 -*-
# @Author: bao
# @Date:   2017-01-23 19:46:28
# @Last Modified by:   bao
# @Last Modified time: 2017-02-17 20:21:29

import sys
sys.path.append("..")

from CoreSim.Topology import *
from CoreSim.Node import *
from CoreSim.Link import *
import networkx as nx

SERVER = 0
TOR = 1

class Jellyfish(Topology):
	"""
	Jellyfish topology: k-regualr random graph
	numOfHost: host number per ToR switch (Rack)
	numOfHosts: the whole host number
	"""
	def __init__(self, numOfHost, numOfLink, numOfToRs, k=2):
		# initialize nodes and links
		Topology.__init__(self)
		self.numOfHost = numOfHost
		self.numOfLink = numOfLink
		self.numOfToRs = numOfToRs
		self.numOfHosts = numOfHost * numOfToRs
		self.com_network = nx.DiGraph() # Including all the information of the network, host and switch
		self.k = k # k-shortest-path threshold

	def CreateTopology(self):
		'''
		create nodes
		create links
		'''
		self.CreateNodes()
		self.CreateLinks()

	def CreateLinks(self):
 		"""
        We build this topology as a directed graph.
        It indicates n1 --- n2 will translate into to edges: (1, 2) and (2, 1)
        """
		for hostId in range(1, self.numOfHosts+1):
			s2torId = (hostId-1)/self.numOfHost + 1
			torNodeId = self.ConvertToNodeId(s2torId, TOR)
			self.links[hostId, torNodeId] = Link((hostId, torNodeId))
			self.links[torNodeId, hostId] = Link((torNodeId, hostId))
			
		regular_graph = nx.random_regular_graph(self.numOfLink, self.numOfToRs) # node id starts from 0
		while not nx.is_connected(regular_graph):
			regular_graph = nx.random_regular_graph(self.numOfLink, self.numOfToRs)

		for src,dst in regular_graph.edges():
			self.com_network.add_edge(src+1, dst+1, weight=1)
			self.com_network.add_edge(dst+1, src+1, weight=1)
			srcTorID = self.ConvertToNodeId(src+1, TOR)
			dstToRID = self.ConvertToNodeId(dst+1, TOR)
			self.links[srcTorID, dstToRID] = Link((srcTorID, dstToRID))
			self.links[dstToRID, srcTorID] = Link((dstToRID, srcTorID))

	
	def CreateNodes(self):
		# node id is start from 1
		self.nodes.append(None)
		# append server node
		self.AddNodes(self.numOfHosts)
		# append tor switch node
		self.AddNodes(self.numOfToRs)

	def ConvertToNodeId(self, id, role):
		"""
        Convert regular device id into node id.
        Four roles are defined: 0:server, 1:tor switch
        """
		if role == 0:
			return id
		else:
			return id + self.numOfHosts

	def AddNodes(self, n):
		for id in range(1, n + 1):
			node = Node()
			node.nodeId = len(self.nodes)
			self.nodes.append(node)

	# return corresponding role
	def GetServerNode(self, serverId):
		nodeId = self.ConvertToNodeId(serverId, SERVER)
		return self.nodes[nodeId]
	
	def GetToRNode(self, torID):
		nodeId = self.ConvertToNodeId(torId, TOR)
		return self.nodes[nodeId]
	def GetToRId(self, hostId):
		return (hostId-1)/self.numOfHost + 1